#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import os
import sys
import json
import uuid
import signal
import shutil
import logging
from gi.repository import GLib
from dbus.mainloop.glib import DBusGMainLoop
from wrt_util import WrtUtil
from wrt_param import WrtConst
from wrt_common import WrtCommon
from wrt_plugin import WrtPluginHub
from wrt_prefix_pool import PrefixPool
from wrt_dbus import DbusMainObject
from wrt_dbus import DbusIpForwardObject
from wcore_lv01 import WrtCoreLv01
from wcore_lv02 import WrtCoreLv02
from wcore_lv99 import WrtCoreLv99
from wcore_lv01 import DefaultSelectorLv01
from wcore_lv02 import DefaultSelectorLv02
from wcore_lv99 import DefaultSelectorLv99


class WrtDaemon:

    def __init__(self, param):
        self.param = param
        self.bRestart = False

    def run(self):
        WrtUtil.ensureDir(self.param.varDir)
        WrtUtil.mkDirAndClear(self.param.tmpDir)
        WrtUtil.mkDirAndClear(self.param.runDir)
        try:
            logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
            logging.getLogger().setLevel(WrtUtil.getLoggingLevel(self.param.logLevel))
            logging.info("Program begins.")

            # manipulate iptables
            if not self.param.abortOnError:
                WrtUtil.iptablesSetEmpty()
            else:
                if not WrtUtil.iptablesIsEmpty():
                    raise Exception("iptables is not empty, wrtd use iptables exclusively")

            # load configuration
            self._loadCfg()

            # load UUID
            if self._loadUuid(self.param):
                logging.info("UUID generated: \"%s\"." % (self.param.uuid))
            else:
                logging.info("UUID loaded: \"%s\"." % (self.param.uuid))

            # write pid file
            with open(self.param.pidFile, "w") as f:
                f.write(str(os.getpid()))

            # create main loop
            DBusGMainLoop(set_as_default=True)
            self.param.mainloop = GLib.MainLoop()

            # load plugin hub
            self.param.pluginHub = WrtPluginHub(self.param)
            logging.info("Plugin HUB loaded.")

            # load prefix pool
            self.param.prefixPool = PrefixPool(os.path.join(self.param.varDir, "prefix-pool.json"))
            logging.info("Prefix pool loaded.")

            # business initialize
            p = self.param.pluginHub.getSelectorPlugin()
            if p is not None:
                self.param.selectorLv01 = p.get_selector_lv01()
                self.param.selectorLv02 = p.get_selector_lv02()
                self.param.selectorLv99 = p.get_selector_lv99()
            else:
                self.param.selectorLv01 = DefaultSelectorLv01()
                self.param.selectorLv02 = DefaultSelectorLv02()
                self.param.selectorLv99 = DefaultSelectorLv99()
            self.param.wcoreLv01 = WrtCoreLv01(self.param, self.param.selectorLv01, self.param.selectorLv02, self.param.selectorLv99)
            self.param.wcoreLv02 = WrtCoreLv02(self.param, self.param.selectorLv02, self.param.selectorLv99)
            self.param.wcoreLv99 = WrtCoreLv99(self.param, self.param.selectorLv99)

            # enable ip forward
            if WrtUtil.readFile(WrtConst.procIpForwareFile) == "0":
                with open(WrtConst.procIpForwareFile, "w") as f:
                    f.write("1")
                logging.info("IP forwarding enabled.")
            else:
                logging.warn("IP forwarding already enabled.")

            # start DBUS API server
            self.param.dbusMainObject = DbusMainObject(self.param)
            self.param.dbusIpForwardObject = DbusIpForwardObject(self.param)
            logging.info("DBUS-API server started.")

            # start main loop
            logging.info("Mainloop begins.")
            GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, self._sigHandlerINT, None)
            GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, self._sigHandlerTERM, None)
            GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGHUP, self._sigHandlerHUP, None)
            self.param.mainloop.run()
            logging.info("Mainloop exits.")
        finally:
            if self.param.subnetManager is not None:
                self.param.subnetManager.dispose()
                self.param.subnetManager = None
            if self.param.connectionManager is not None:
                self.param.connectionManager.dispose()
                self.param.connectionManager = None
            if self.param.trafficManager is not None:
                self.param.trafficManager.dispose()
                self.param.trafficManager = None
            logging.shutdown()
            shutil.rmtree(self.param.tmpDir)
            if self.bRestart:
                WrtUtil.restartProgram()

    def _sigHandlerINT(self, signum):
        logging.info("SIGINT received.")
        self.param.mainloop.quit()
        return True

    def _sigHandlerTERM(self, signum):
        logging.info("SIGTERM received.")
        self.param.mainloop.quit()
        return True

    def _sigHandlerHUP(self, signum):
        logging.info("SIGHUP received.")
        self.bRestart = True
        self.param.mainloop.quit()
        return True

    def _loadCfg(self):
        pass

    def _loadUuid(self):
        if os.path.exists(self.param.dataFile):
            cfgObj = None
            with open(self.param.dataFile, "r") as f:
                cfgObj = json.load(f)
            self.param.uuid = cfgObj["uuid"]
            return False
        else:
            self.param.uuid = WrtCommon._generateAndSaveUuid()
            return True

    def _generateAndSaveUuid(self):
        cfgObj = dict()
        cfgObj["uuid"] = str(uuid.uuid4())
        with open(self.param.dataFile, "w") as f:
            json.dump(cfgObj, f)
        return cfgObj["uuid"]
